package com.winter.common.dynamic.datasource;

import com.baomidou.dynamic.datasource.DynamicDataSourceConfigure;
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAdvisor;
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationAdvisor;
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor;
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
import com.baomidou.dynamic.datasource.creator.DruidDataSourceCreator;
import com.baomidou.dynamic.datasource.creator.HikariDataSourceCreator;
import com.baomidou.dynamic.datasource.processor.DsHeaderProcessor;
import com.baomidou.dynamic.datasource.processor.DsProcessor;
import com.baomidou.dynamic.datasource.processor.DsSessionProcessor;
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.YmlDynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidDynamicDataSourceConfiguration;
import com.winter.common.dynamic.datasource.service.DataSourceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.sql.DataSource;
import java.util.Map;

@Configuration
@EnableConfigurationProperties({DynamicDataSourceProperties.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@Import({DruidDynamicDataSourceConfiguration.class})
@ConditionalOnProperty(
        prefix = "spring.datasource.dynamic",
        name = {"enabled"},
        havingValue = "true",
        matchIfMissing = true
)
//@EnableAutoConfiguration(exclude = com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration.class)
@Order(1)
@RestController
@RequestMapping("datasource")
public class DynamicDataSourceAutoConfiguration {
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAutoConfiguration.class);
    @Autowired
    private DynamicDataSourceProperties properties;
    @Autowired(
            required = false
    )
    private ApplicationContext applicationContext;

    private DataSourceCreator dataSourceCreator;

    private DynamicDataSourceProvider dynamicDataSourceProvider;

    private DynamicRoutingDataSource dynamicRoutingDataSource;

    @Autowired
    DataSourceService dataSourceService;

    public DynamicDataSourceAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public DynamicDataSourceProvider dynamicDataSourceProvider() {
        this.dynamicDataSourceProvider = new YmlDynamicDataSourceProvider(this.properties);
        return this.dynamicDataSourceProvider;
    }

    @Bean
    @ConditionalOnMissingBean
    public DataSourceCreator dataSourceCreator() {
        DataSourceCreator dataSourceCreator = new DataSourceCreator();
        dataSourceCreator.setDruidDataSourceCreator(new DruidDataSourceCreator(this.properties.getDruid(), this.applicationContext));
        dataSourceCreator.setHikariDataSourceCreator(new HikariDataSourceCreator(this.properties.getHikari()));
        dataSourceCreator.setGlobalPublicKey(this.properties.getPublicKey());
        this.dataSourceCreator = dataSourceCreator;
        return dataSourceCreator;
    }

    @Bean
    @ConditionalOnMissingBean
    public DynamicRoutingDataSource dynamicRoutingDataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(this.properties.getPrimary());
        dataSource.setStrategy(this.properties.getStrategy());
        dataSource.setProvider(dynamicDataSourceProvider);
        dataSource.setP6spy(this.properties.getP6spy());
        dataSource.setStrict(this.properties.getStrict());
        dataSource.setSeata(this.properties.getSeata());
        this.dynamicRoutingDataSource = dataSource;
        return dataSource;
    }
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource(DynamicRoutingDataSource dataSource) {
        return dataSource;
    }

    @Bean
    @ConditionalOnMissingBean
    public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
        DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
        interceptor.setDsProcessor(dsProcessor);
        DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
        advisor.setOrder(this.properties.getOrder());
        return advisor;
    }

    @Bean
    @ConditionalOnMissingBean
    public DsProcessor dsProcessor() {
        DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
        DsSessionProcessor sessionProcessor = new DsSessionProcessor();
        DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
        headerProcessor.setNextProcessor(sessionProcessor);
        sessionProcessor.setNextProcessor(spelExpressionProcessor);
        return headerProcessor;
    }

    @Bean
    @ConditionalOnBean({DynamicDataSourceConfigure.class})
    public DynamicDataSourceAdvisor dynamicAdvisor(DynamicDataSourceConfigure dynamicDataSourceConfigure, DsProcessor dsProcessor) {
        DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(dynamicDataSourceConfigure.getMatchers());
        advisor.setDsProcessor(dsProcessor);
        advisor.setOrder(-2147483648);
        return advisor;
    }

    /**
     * 获取当前所有数据源
     * @return
     */
    @GetMapping("list")
    public Map listCurrentDataSource(){
//        Map<String, DataSource> dataSourceMap = this.dynamicRoutingDataSource.getDataSourceMap();
//        return this.properties.getDatasource();
        return dataSourceService.listCurrentDataSource();
    }

    /**
     * 新增数据源
     * @param dataSourceProperty
     * @return
     */
    @GetMapping("add")
    public boolean addDataSource(DataSourceProperty dataSourceProperty){
//        DataSource dataSource = this.dataSourceCreator.createDataSource(dataSourceProperty);
//        Map<String, DataSourceProperty> map = this.properties.getDatasource();
//        dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPollName(),dataSource);
//        map.put(dataSourceProperty.getPollName(),dataSourceProperty);
//        return true;
        return dataSourceService.addDataSource(dataSourceProperty);
    }

    /**
     * 获取默认数据源信息
     * @return
     */
    @GetMapping("default")
    public DataSourceProperty defaultDataSource(){
//        this.properties.getPrimary();
//        String primary = dynamicRoutingDataSource.getPrimary();
//        return this.properties.getDatasource().get(primary);
        return dataSourceService.defaultDataSource();
    }
    @GetMapping("current")
    public DataSourceProperty currentDataSource(){
//        this.properties.getPrimary();
//        String primary = dynamicRoutingDataSource.getPrimary();
//        return this.properties.getDatasource().get(primary);
        return dataSourceService.currentDataSource();
    }

    /**
     * 切换默认数据源
     * @param alias
     * @return
     */
    @GetMapping("switch")
    public boolean setDefaultDataSource(String alias){
//        Map<String, DataSourceProperty> map = this.properties.getDatasource();
//        if(!map.containsKey(alias)){
//            return false;
//        }
//        dynamicRoutingDataSource.setPrimary(alias);
//        this.properties.setPrimary(alias);
//        return true;
        return dataSourceService.setDefaultDataSource(alias);
    }
}
